# -*- coding: utf8 -*-

from twisted.words.protocols.jabber import client, jid, xmlstream
from twisted.words.xish import domish
from twisted.python.log import msg, err
from core.plugmgr import pluginManager


class nonStrictExpatElementStream(domish.ExpatElementStream):
    """
    Based on twisted.words.xish.domish.ExpatElementStream, but parses
    namespaces manually, without help of pyexpat, so it doesn't crash
    because of unbound prefixes.
    """
    def __init__(self):
        import pyexpat
        self.strict = False
        self.DocumentStartEvent = None
        self.ElementEvent = None
        self.DocumentEndEvent = None
        self.error = pyexpat.error
        self.parser = pyexpat.ParserCreate("UTF-8")
        self.parser.StartElementHandler = self._onStartElement
        self.parser.EndElementHandler = self._onEndElement
        self.parser.CharacterDataHandler = self._onCdata
        self.currElem = None
        self.defaultNsStack = ['']
        self.documentStarted = 0
        self.reservedPrefixes = {'xml': 'http://www.w3.org/XML/1998/namespace',\
        'xmlns': 'http://www.w3.org/2000/xmlns/'}
        self.localPrefixesStack = [{}]
    def getUriByPrefix(self, q):
      prefix = q[0]
      nname = q[1]
      uri = None
      i = len(self.localPrefixesStack) - 1
      while (i>=0) and (uri is None):
        uri = self.localPrefixesStack[i].get(prefix, None)
        i -= 1
      if uri is None:
        if prefix.lower().startswith('xml'):
          uri = self.reservedPrefixes.get(prefix, self.defaultNsStack[-1])
        elif self.strict: raise ParserError, 'Unbound prefix: ' + prefix
        else: uri = self.defaultNsStack[-1]
      return (uri, nname)
    def _onStartElement(self, name, attrs):
        # Push default uri into stack
        defaultNs = attrs.pop('xmlns', None)
        if defaultNs is None: self.defaultNsStack.append(self.defaultNsStack[-1])
        else: self.defaultNsStack.append(defaultNs)
        self.localPrefixesStack.append({})
        
        # Check for local prefixes
        for k, v in attrs.items():
            if k.startswith('xmlns:'):
                 prefix = k[6:]
                 self.localPrefixesStack[-1][prefix] = v
                 del attrs[k]
        
        # Generate a qname tuple from the provided name
        qname = name.split(":")
        if len(qname) == 1: qname = (self.defaultNsStack[-1], name)
        else: qname = self.getUriByPrefix(qname)

        # Process attributes
        for k, v in attrs.items():
            if k.find(":") != -1:
                aqname = k.split(":")
                attrs[self.getUriByPrefix(aqname)] = v
                del attrs[k]

        # Construct the new element
        e = domish.Element(qname, self.defaultNsStack[-1], attrs, self.localPrefixesStack[-1])

        # Document already started
        if self.documentStarted == 1:
            if self.currElem != None:
                self.currElem.children.append(e)
                e.parent = self.currElem
            self.currElem = e

        # New document
        else:
            self.documentStarted = 1
            self.DocumentStartEvent(e)
    def _onEndElement(self, _):
        domish.ExpatElementStream._onEndElement(self, _)
        self.defaultNsStack.pop()
        self.localPrefixesStack.pop()

class myXmlStream(xmlstream.XmlStream):
 def _initializeStream(self):
        """ Sets up XML Parser. """
        self.stream = nonStrictExpatElementStream()
        self.stream.DocumentStartEvent = self.onDocumentStart
        self.stream.ElementEvent = self.onElement
        self.stream.DocumentEndEvent = self.onDocumentEnd

class eggrevenger(xmlstream.XmlStreamFactory):
    protocol = myXmlStream
    stream = None
    shutting_down = False
    def __init__(self, config, reactor):
        self.config = config
        self.reactor = reactor
        self.jid = jid.JID(u'%s@%s/%s' % (config.username, config.server, config.resource))
        authenticator = client.XMPPAuthenticator(self.jid, config.password)
        xmlstream.XmlStreamFactory.__init__(self, authenticator)
        self.plugmgr = pluginManager(self)
        self.plugmgr.restore()
        self.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT, self.connected)
        self.addBootstrap(xmlstream.STREAM_END_EVENT, self.disconnected)
        self.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self.authenticated)
        self.addBootstrap(xmlstream.INIT_FAILED_EVENT, self.initFailed)
        self.reactor.addSystemEventTrigger('before', 'shutdown', self.beforeShutdown)
    def clientConnectionFailed(self, connector, reason):
        """Do not stop reactor, but try to reconnect"""
        err("Can't connect to Jabber server (details below)")
        err(reason)
        self.retry(connector)
    def connected(self, stream):
        self.stream = stream
        msg("Connected to Jabber server.")
    def disconnected(self, stream):
        """don't worry, we'll reconnect"""
        self.stream = None
        if not self.shutting_down:
            self.plugmgr.disconnected()
            err("Disconnected!")
    def initFailed(self, stream):
        err("Xml stream initialization failed. (invalid username/password?)")
        err("Check authentication data in config file and restart me.")
        self.reactor.stop()
    def authenticated(self, stream):
        msg("Successfully authenticated!")
        self.stream = stream
        self.plugmgr.connected()
    def beforeShutdown(self):
        """Эта функция будет вызвана перед остановкой бота"""
        msg("Eggrevenger is shutting down NOW.")
        self.shutting_down = True
        self.plugmgr.unloadAll()
        self.plugmgr = None
        self.stopTrying()
    def __getitem__(self, k):
        return self.plugmgr.__getitem__(k)
    def get(self, k, v=None):
        return self.plugmgr.get(k, v)
    def has(self, plugin):
        return self.plugmgr.has_key(plugin)
